package com.shensi.adminCollect.controller.payApi;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shensi.common.core.domain.AjaxResult;
import com.shensi.common.core.domain.ResultData;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

/**
 * @date 2023/10/19
 */
@Component
public class QujiaPayApiRedirectController {

    private static final Logger log = LoggerFactory.getLogger(QujiaPayApiRedirectController.class);

    private static final String API_PREFIX = "/payApi";

    @Autowired
    private RestTemplate restTemplate;
    @Value("${qujia.payBaseUrl}")
    private String payBaseUrl;


    /**
     * 上传form表单，文件
     */
    private final static String CONTENT_TYPE_FORM = "multipart/form-data;";


    public void redirectUrl(HttpServletRequest request,HttpServletResponse response){
        String body=JSON.toJSONString(AjaxResult.error("服务异常"));
        try {
            log.info("开始转发请求");
            ResponseEntity<String> resEntity = redirect(request, payBaseUrl);
            body = resEntity.getBody();
            if (body == null) {
                body = JSON.toJSONString(AjaxResult.error("服务异常"));
            }else{
                JSONObject jsonObject = JSONObject.parseObject(body);
                Integer retCode = jsonObject.getInteger("retCode");
                if (retCode != null) {
                    String message = jsonObject.getString("message");
                    jsonObject.put("code",retCode==0? ResultData.SUCCESS_CODE:retCode);
                    jsonObject.put("msg",message);
                    body = jsonObject.toJSONString();
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        try {
            response.setHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE);
            StreamUtils.copy(body,StandardCharsets.UTF_8,response.getOutputStream());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * 请求转发统一处理
     * @param request
     * @param routeUrl 路由地址，统一前缀
     * @return
     * @throws Exception
     */
    public ResponseEntity<String> redirect(HttpServletRequest request, String routeUrl) throws Exception {
        String contentType = request.getContentType();
        log.info("getContentType={}", contentType);
        // multipart/form-data处理
        if (StringUtils.isNotEmpty(contentType) && contentType.contains(CONTENT_TYPE_FORM)) {
            return redirectFile(request, routeUrl);
        } else {
            return redirect(request, routeUrl, API_PREFIX);
        }
    }

    /**
     * 上传form表单，文件
     *
     * 请求转发处理
     * @param request
     * @param routeUrl
     * @return
     * @throws IOException
     */
    public ResponseEntity<String> redirectFile(HttpServletRequest request, String routeUrl) throws IOException {
        // build up the redirect URL
        String redirectUrl = createRedictUrl(request, routeUrl, API_PREFIX);
        log.info("redirectFile redirectUrl={}", redirectUrl);
        String method = request.getMethod();
        //设置请求头
        MultiValueMap<String, String> headers = parseRequestHeader(request);

        // 组装form参数
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        StandardMultipartHttpServletRequest standardMultipartHttpServletRequest = (StandardMultipartHttpServletRequest)request;
        // 组装form参数-文件
        MultiValueMap<String, MultipartFile> multiValueMap = standardMultipartHttpServletRequest.getMultiFileMap();
        for (Map.Entry<String, List<MultipartFile>> entries : multiValueMap.entrySet()) {
            for (MultipartFile multipartFile : entries.getValue()) {
                String fileName = multipartFile.getOriginalFilename();
                log.info("redirectFile MultipartFile: fileName={}", fileName);
                File file = File.createTempFile("spw-", fileName);
                multipartFile.transferTo(file);
                FileSystemResource fileSystemResource = new FileSystemResource(file);
                form.add(entries.getKey(), fileSystemResource);
            }
        }
        // 组装form参数-一般属性
        Enumeration<String> enumeration = standardMultipartHttpServletRequest.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = standardMultipartHttpServletRequest.getParameter(name);
            log.info("redirectFile enumeration: name={}, value={}", name, value);
            form.add(name, value);
        }

        // 用HttpEntity封装整个请求报文
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(form, headers);

        return restTemplate.exchange(redirectUrl, HttpMethod.valueOf(method),formEntity,String.class);
    }

    /**
     * 非form-data请求转发处理
     * @param request
     * @param routeUrl
     * @param prefix
     * @return
     * @throws Exception
     */
    public ResponseEntity<String> redirect(HttpServletRequest request, String routeUrl, String prefix) throws Exception {
        // build up the redirect URL
        String redirectUrl = createRedictUrl(request,routeUrl, prefix);
        log.info("redirectUrl={}", redirectUrl);
        RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
        return restTemplate.exchange(requestEntity, String.class);
    }

    /**
     * 构建重定向地址
     * @param request
     * @param routeUrl
     * @param prefix
     * @return
     */
    private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
        String queryString = request.getQueryString();
        return routeUrl + request.getRequestURI().replace(prefix, "") +
                (queryString != null ? "?" + queryString : "");
    }

    /**
     * 构建请求实体
     * @param request
     * @param url
     * @return
     * @throws URISyntaxException
     * @throws IOException
     */
    private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }

    /**
     * 解析请求体
     * @param request
     * @return
     * @throws IOException
     */
    private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    }

    /**
     * 解析请求头
     * @param request
     * @return
     */
    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) {
        HttpHeaders headers = new HttpHeaders();
        List<String> headerNames = Collections.list(request.getHeaderNames());
        boolean hasContentType = false;
        for (String headerName : headerNames) {
            hasContentType = hasContentType || StringUtils.equals(HttpHeaders.CONTENT_TYPE,headerName);
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            for (String headerValue : headerValues) {
                headers.add(headerName, headerValue);
            }
        }
        if (!hasContentType) {
            headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        }
        // 移除host, 否则本地调试会报错
        headers.remove(HttpHeaders.HOST);
        return headers;
    }

}
